package dslProgramGen.util;

import dslProgramGen.DslExpressedMethod;
import dslProgramGen.GlobalVarTableRecord;
import dslProgramGen.WhoPointToThisObjectCollection;
import methodCallTreeGen.MethodNode;
import methodCallTreeGen.Node;
import methodCallTreeGen.NodeType;
import methodCallTreeGen.TextNode;
import utils.Utils;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.*;

import static dslProgramGen.DslProgramGen.isGenJavaProgram;
import static methodCallTreeGen.ProcessBeforeMainAndRemovedNodeTraces.globalVarTable;
import static utils.Utils.RegularExpressionProcess;
import static utils.Utils.writeLineToDstFile;

public class DslProgramGenUtil {
    /**
     * 全局变量所在的类名前缀
     */
    public static final String mainClassNamePre = "Main";
    /**
     * 全局变量字段名前缀
     */
    public static final String globalVarNamePre = "globalVar";
    /**
     * 每个Maini类里的静态字段数目
     */
    public static final long staticFieldsNumPerMainiClass = 10000;
    /**
     * 主类index，若一个Main类的静态字段数目超过staticFieldsNumPerMainClass，mainClassIndex++
     */
    public static long mainClassIndex = 0;
    /**
     * 区分一个Maini类里的变量名
     */
    public static long globalVarIndex = 0;

    /**
     * 打印一个MethodNode的DSL内容
     *
     * @param methodNode  待打印的MethodNode
     * @param dstFilePath 输出DSL程序到文件路径
     */
    public static void printAndWriteOneMethodInDsl(MethodNode methodNode, String dstFilePath) {
        if (methodNode.isWriteMethodDefineToFile) {
            // 如果相同的函数定义已经输出一次了，不用再输出了
            return;
        }
        DslExpressedMethod dslExpressedMethod = methodNode.dslExpressedMethod;

//        System.out.println("函数: " + dslExpressedMethod.methodName + " 的DSL表示:");
        String parameters = "(";
        for (int i = 0; i < dslExpressedMethod.parameters.size(); i++) {
            if (i == dslExpressedMethod.parameters.size() - 1) {
                parameters += dslExpressedMethod.parameters.get(i);
                break;
            }
            parameters += dslExpressedMethod.parameters.get(i) + ", ";
        }
        parameters += ") {";
        String methodFirstLine = dslExpressedMethod.returnType + " " + dslExpressedMethod.methodName + parameters;
//        System.out.println(methodFirstLine);
        writeLineToDstFile(methodFirstLine, dstFilePath);
        //获取参数列表并新建参数相关局部变量
        Set<String> parSet = new HashSet<>();
        for(String s: dslExpressedMethod.parameters){
            if(s.equals("int iNdex"))
                continue;
            parSet.add(s.split(" ")[1]);
            writeLineToDstFile("    " + s + "1111 = " + s.split(" ")[1] + ";", dstFilePath);
        }

        for (String content : dslExpressedMethod.methodContentList) {
//            System.out.println("    " + content);
            String line = content;
            String[] content1 = content.split(" = ");
            if((content1.length > 1) && parSet.contains(content1[0]))
                line = content.replace(" = ","1111 = ");
            writeLineToDstFile("    " + line, dstFilePath);
        }
//        System.out.println("}");
        writeLineToDstFile("}", dstFilePath);
        writeLineToDstFile("", dstFilePath);
//        System.out.println();
//        System.out.println();
        // 表示函数定义已经输出过一次了
        methodNode.isWriteMethodDefineToFile = true;
    }

    /**
     * 打印每个函数的DSL语句
     *
     * @param root        函数调用树的根节点
     * @param dstFilePath 输出DSL程序到所在的文件路径
     */
    public static void printAndWriteAllMethodInDsl(MethodNode root, String dstFilePath) {
        printAndWriteOneMethodInDsl(root, dstFilePath);
        for (Node node : root.contentNodeList) {
            if (node.nodeType == NodeType.METHOD) {
                // Node为MethodNode
                printAndWriteAllMethodInDsl((MethodNode) node, dstFilePath);
            }
        }
    }

    /**
     * 从全局变量表文件中读取全局变量表
     * @param globalVarTableFilePath
     * @throws FileNotFoundException
     */
    public static void readGlobleVarTable(String globalVarTableFilePath) throws FileNotFoundException {
        Scanner scanner = new Scanner(new FileReader(globalVarTableFilePath));
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            String[] split = line.split(" ");
            if ("null".equals(split[3])) {
                globalVarTable.put(split[0], new GlobalVarTableRecord(split[0], split[1], split[2], null, true));
            } else {
                globalVarTable.put(split[0], new GlobalVarTableRecord(split[0], split[1], split[2], split[3], true));
            }
        }
        scanner.close();
    }

    /**
     * 给定一个类名，获得以该类名构建的local root的序号
     *
     * @param className          给定类名
     * @param classTypeAndNumMap local root命名的辅助结构， 对于新建local root例如Student stu，如何命名stu，当前采用 "student"+num的方式表示
     * @return local root的序号
     */
    public static long getLocalRootNum(String className, Map<String, Long> classTypeAndNumMap) {
        // 如果classTypeAndNumMap的keys没有className
        if (!classTypeAndNumMap.keySet().contains(className)) {
            classTypeAndNumMap.put(className, (long) 0);
            return 0;
        } else {
            // 如果classTypeAndNumMap的keys有className
            Long num = classTypeAndNumMap.get(className);
            classTypeAndNumMap.put(className, num + 1);
            return num + 1;
        }
    }

    /**
     * 获得当前TextNode的下一个下一个节点
     *
     * @param currentTextNode 当前待处理的TextNode
     * @return 当前TextNode的下一个下一个节点
     */
    public static Node getNextNextSiblingNodeOfCurrentTextNode(TextNode currentTextNode) {
        List<Node> contentNodeListOfCurrentParentMethodNode = ((MethodNode) currentTextNode.parentNode).contentNodeList;
        // 获得当前TextNode在contentNodeListOfCurrentParentMethodNode中的index
        int currentTextNodeIndex = contentNodeListOfCurrentParentMethodNode.indexOf(currentTextNode);
        if (contentNodeListOfCurrentParentMethodNode.size() > currentTextNodeIndex + 2) {
            return contentNodeListOfCurrentParentMethodNode.get(currentTextNodeIndex + 2);
        } else {
            return null;
        }
    }


    /**
     * 获得当前Node的下一个节点
     *
     * @param currentNode 当前待处理的Node
     * @return 当前Node的下一个节点
     */
    public static Node getNextSiblingNodeOfCurrentNode(Node currentNode) {
        List<Node> contentNodeListOfCurrentParentMethodNode = ((MethodNode) currentNode.parentNode).contentNodeList;
        // 获得当前Node在contentNodeListOfCurrentParentMethodNode中的index
        int currentTextNodeIndex = contentNodeListOfCurrentParentMethodNode.indexOf(currentNode);
        if (currentTextNodeIndex == contentNodeListOfCurrentParentMethodNode.size() - 1) {
            // 如果当前Node没有下一个Node，即当前Node是最后一个Node
            return null;
        }
        return contentNodeListOfCurrentParentMethodNode.get(currentTextNodeIndex + 1);
    }

    /**
     * 判断当前Node是否有下一个节点
     *
     * @param currentNode 当前待处理的Node
     * @return true表示当前Node有下一个节点
     */
    public static boolean hasNextSiblingNodeOfCurrentNode(Node currentNode) {
        List<Node> contentNodeListOfCurrentParentMethodNode = ((MethodNode) currentNode.parentNode).contentNodeList;
        // 获得当前Node在contentNodeListOfCurrentParentMethodNode中的index
        int currentTextNodeIndex = contentNodeListOfCurrentParentMethodNode.indexOf(currentNode);
        if (currentTextNodeIndex + 1 < contentNodeListOfCurrentParentMethodNode.size()) {
            return true;
        } else {
            return false;
        }
    }


    /**
     * 给定一个objId，获得它在DSL程序中的一种表示，如果局部变量表达式是从Get中获得的，要移除Get记录
     *
     * @param objId                   给定一个objId
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @return 给定对象Id的一种DSL表示
     */
    public static String getDslExpressionOfObj(String objId, Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap) {
        // 首先在全局变量表中找有没有
        GlobalVarTableRecord globalVarTableRecord = globalVarTable.get(objId);
        if (globalVarTableRecord != null) {
            return globalVarTableRecord.dslExpr;
        }
        WhoPointToThisObjectCollection whoPointToThisObjectCollection = objId2objExpressionsMap.get(objId);
        if (whoPointToThisObjectCollection != null) {
            // 局部变量表中可能有
            List<String> lrAndPrPointToThisObjectList = whoPointToThisObjectCollection.lrAndPrPointToThisObjectList;
            if (lrAndPrPointToThisObjectList.size() != 0) {
                return lrAndPrPointToThisObjectList.get(0);
            }
            List<String> fieldOfObjExpressionsPointToThisObjectList = whoPointToThisObjectCollection.fieldOfObjExpressionsPointToThisObjectList;
            if (fieldOfObjExpressionsPointToThisObjectList.size() != 0) {
                return fieldOfObjExpressionsPointToThisObjectList.get(0);
            }
            List<String> newForReturnTempLocalRootList = whoPointToThisObjectCollection.newForReturnTempLocalRootList;
            if (newForReturnTempLocalRootList.size() != 0) {
                return newForReturnTempLocalRootList.get(0);
            }
            List<String> newForMethodParameterTempLocalRootList = whoPointToThisObjectCollection.newForMethodParameterTempLocalRootList;
            if (newForMethodParameterTempLocalRootList.size() != 0) {
                return newForMethodParameterTempLocalRootList.get(0);
            }
            List<String> methodReturnForReturnTempLocalRootList = whoPointToThisObjectCollection.methodReturnForReturnTempLocalRootList;
            if (methodReturnForReturnTempLocalRootList.size() != 0) {
                return methodReturnForReturnTempLocalRootList.get(0);
            }
            List<String> methodReturnForMethodParameterTempLocalRootList = whoPointToThisObjectCollection.methodReturnForMethodParameterTempLocalRootList;
            if (methodReturnForMethodParameterTempLocalRootList.size() != 0) {
                return methodReturnForMethodParameterTempLocalRootList.get(0);
            }
            List<String> getTracesRecordList = whoPointToThisObjectCollection.getTracesRecordList;
            if(getTracesRecordList.size() != 0){
                String tem = getTracesRecordList.get(0);
                getTracesRecordList.remove(0);
                return tem;
            }

        }
        // 在局部变量表和全局变量表中都找不到
        return null;
    }


    /**
     * 给定一个objId，尝试获得它在DSL程序中的一种表示，只获得，不做任何移除操作，主要是对Get有影响
     *
     * @param objId                   给定一个objId
     * @param objId2objExpressionsMap 指定对象id在DSL程序中可以怎样表示出来 <objId, expressions>
     * @return 给定对象Id的一种DSL表示
     */
    public static String tryToGetDslExpressionOfObj(String objId, Map<String, WhoPointToThisObjectCollection> objId2objExpressionsMap) {
        // 首先在全局变量表中找有没有
        GlobalVarTableRecord globalVarTableRecord = globalVarTable.get(objId);
        if (globalVarTableRecord != null) {
            // 在全局变量表中找到了
            // 在全局变量表中找到了
            // 如果使用过，直接返回DSL表达式
            if (globalVarTableRecord.isUsed) {
                return globalVarTableRecord.dslExpr;
            } else {
                // 没有使用过，生成globalVarTableRecord的DSL表达式，isUsed为true
                globalVarTableRecord.isUsed = true;

                String dslExpr = mainClassNamePre + mainClassIndex + "." + globalVarNamePre + "_" + globalVarIndex;
                globalVarIndex++;
                if (globalVarIndex == staticFieldsNumPerMainiClass) {
                    // 达到一个类最大静态字段数
                    mainClassIndex++;
                    globalVarIndex = 0;
                }
                globalVarTableRecord.dslExpr = dslExpr;
                return dslExpr;
            }
        }
        WhoPointToThisObjectCollection whoPointToThisObjectCollection = objId2objExpressionsMap.get(objId);
        if (whoPointToThisObjectCollection != null) {
            // 局部变量表中可能有
            List<String> lrAndPrPointToThisObjectList = whoPointToThisObjectCollection.lrAndPrPointToThisObjectList;
            if (lrAndPrPointToThisObjectList.size() != 0) {
                return lrAndPrPointToThisObjectList.get(0);
            }
            List<String> fieldOfObjExpressionsPointToThisObjectList = whoPointToThisObjectCollection.fieldOfObjExpressionsPointToThisObjectList;
            if (fieldOfObjExpressionsPointToThisObjectList.size() != 0) {
                return fieldOfObjExpressionsPointToThisObjectList.get(0);
            }
            List<String> newForReturnTempLocalRootList = whoPointToThisObjectCollection.newForReturnTempLocalRootList;
            if (newForReturnTempLocalRootList.size() != 0) {
                return newForReturnTempLocalRootList.get(0);
            }
            List<String> newForMethodParameterTempLocalRootList = whoPointToThisObjectCollection.newForMethodParameterTempLocalRootList;
            if (newForMethodParameterTempLocalRootList.size() != 0) {
                return newForMethodParameterTempLocalRootList.get(0);
            }
            List<String> methodReturnForReturnTempLocalRootList = whoPointToThisObjectCollection.methodReturnForReturnTempLocalRootList;
            if (methodReturnForReturnTempLocalRootList.size() != 0) {
                return methodReturnForReturnTempLocalRootList.get(0);
            }
            List<String> methodReturnForMethodParameterTempLocalRootList = whoPointToThisObjectCollection.methodReturnForMethodParameterTempLocalRootList;
            if (methodReturnForMethodParameterTempLocalRootList.size() != 0) {
                return methodReturnForMethodParameterTempLocalRootList.get(0);
            }
            List<String> getTracesRecordList = whoPointToThisObjectCollection.getTracesRecordList;
            if(getTracesRecordList.size() != 0){
                String tem = getTracesRecordList.get(0);
//                getTracesRecordList.remove(0);
                return tem;
            }

        }
        // 在局部变量表和全局变量表中都找不到
        return null;
    }


    /**
     * 从数组对象类型字符串中获得其element type
     * 例如输入 [LStudent; [I   [Ljava/lang/Object;  [[LStudent;  [[[LStudent;  [[[I
     * 输出    Student    int  java/lang/Object     Student      Student       I
     *
     * @param arrayObjectType 数组对象类型字符串
     * @return element type
     */
    public static String getElementTypeFromArrayObjectType(String arrayObjectType) {
        // 首先区分element type是基础类型还是对象类型
        if (Utils.isInputContainsPattern(arrayObjectType, "\\[L")) {
            // element type是对象类型
            String elementType = Utils.RegularExpressionProcess(arrayObjectType, "\\[L(.*?);");
            // element type可能出现 java/lang/String，把 '/' 替换为 '__'
            elementType = renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(elementType);
            return elementType;
        } else {
            // element type是基础类型
            // 获得element type开始的index
            int elementTypeStartIndex = arrayObjectType.lastIndexOf("[") + 1;
            String elementType = arrayObjectType.substring(elementTypeStartIndex);
            if (elementType != null) {
                switch (elementType) {
                    case "B":
                        return "byte";
                    case "C":
                        return "char";
                    case "D":
                        return "double";
                    case "F":
                        return "float";
                    case "I":
                        return "int";
                    case "J":
                        return "long";
                    case "S":
                        return "short";
                    case "Z":
                        return "boolean";
                }
            }
            return null;
        }
    }

    /**
     * 类名中可能出现'/',把类名中的'/'替换为'_'
     * 替换类名中java_lang_Object类名为Object
     * @param oldClassName 老的可能带有'/'的类名
     * @return 无'/'的类名
     */
    public static String renameClassNameReplaceSlashWithUnderlineAndRenameObjectClassName(String oldClassName) {
        return oldClassName.replace("/", "_").replace("+","_").replaceAll("java_lang_Object", "Object");
    }

    /**
     * 从数组对象类型字符串中获得其维度
     * 例如输入 [LStudent; [I   [Ljava/lang/Object;  [[LStudent;  [[[LStudent;  [[[I
     * 输出    1          1    1                    2             3             3
     *
     * @param arrayObjectType 数组对象类型字符串
     * @return 维度数字
     */
    public static int getDimNumFromArrayObjectType(String arrayObjectType) {
        return arrayObjectType.lastIndexOf("[") + 1;
    }

    /**
     * 判断类型是不是数组类型
     *
     * @param classType 待判断的类型
     * @return true为数组类型
     */
    public static boolean isArrayType(String classType) {
        return classType.startsWith("[");
    }

    /**
     * 转换数组描述符为数组表达式，例如 [C -> char[]
     * @param arrayObjectType 原数组描述符
     * @return 转换后的数组表达式
     */
    public static String castArraySignaturToArrayExpr(String arrayObjectType) {
        // 获得其element类型
        String elementType = getElementTypeFromArrayObjectType(arrayObjectType);
        // 获得新建数组对象的维度
        int dimNum = getDimNumFromArrayObjectType(arrayObjectType);
        // 构造等号右边的 []表达 squareBracket比数组维度少一个[]
        String squareBracket = "";
        for (int i = 0; i < dimNum - 1; i++) {
            squareBracket += "[]";
        }
        String arrayExpr = elementType + "[]" + squareBracket;
        return arrayExpr;
    }

    /**
     * 对一个数组描述符[LStudent，获得低一维的表达式，例如
     * [[C -> char[]
     * [Ljava_lang_Class; -> java_lang_Class
     * @param arrayObjectType 数组描述符
     * @return 低一维的数组表达式
     */
    public static String getOneDimLowerArrayExpr(String arrayObjectType) {
        // 先获得数组的元素类型
        String elementType = getElementTypeFromArrayObjectType(arrayObjectType);
        // 获得低一维的描述符，不一定是数组
        String lowerArraySignature = arrayObjectType.substring(1);
        if (isArrayType(lowerArraySignature)) {
            // 如果低一维的类型仍然是数组
            String lowerArrayExpr = castArraySignaturToArrayExpr(lowerArraySignature);
            return lowerArrayExpr;
        } else {
            // 低一维的类型不是数组
            return elementType;
        }
    }
    /**
     * 获得 Return语句所在的序号
     *
     * @param tracesList 包含Return语句的trace列表
     * @return Return语句所在的序号
     */
    public static int getReturnTraceNumFromList(List<String> tracesList) {
        for (int i = 0; i < tracesList.size(); i++) {
            if (tracesList.get(i).contains(" Return ")) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 判断函数是否有返回值，通过函数的signature判断
     *
     * @param methodNode 当前判断的函数
     * @return signature的返回值为V，表示没有返回值
     */
    public static boolean hasReturnValue(MethodNode methodNode) {
        // Return H:NULL, C:NULL, T:main, M:java/lang/Object.<init> ()V
        // Return H:0x00000000300f34aa, C:Student, T:main, M:Test.myFunctionNoArgAndReturn ()LStudent;
        // 获得函数signature
        String methodSignature = methodNode.methodName.split(" ")[1];
        // 获得函数返回类型
        String returnType = Utils.RegularExpressionProcess(methodSignature, "\\)(.*?)$");
        switch (returnType) {
            case "V":
            case "B":
            case "C":
            case "D":
            case "F":
            case "I":
            case "J":
            case "S":
            case "Z":
                return false;
            default:
                return true;
        }
//
//        // 获得当前methodNode下的最后一个TextNode，里面包含了Return trace语句
//        TextNode lastTextNode = (TextNode) (methodNode.contentNodeList.get(methodNode.contentNodeList.size() - 1));
//        // 获得 Return语句所在的序号
//        int returnTraceNum = getReturnTraceNumFromList(lastTextNode.tracesList);
//        // 获得Return trace语句
//        String returnTrace = lastTextNode.tracesList.get(returnTraceNum);
//        // 获得Hid
//        String hId = utils.Utils.RegularExpressionProcess(returnTrace, "H:(.*?),");
//        if ("NULL".equals(hId)) {
//            return false;
//        }
//        return true;
    }

    /**
     * 获得一个MethodNode的return trace内容
     *
     * @param methodNode 待处理的MethodNode
     * @return 该MethodNode下的return trace内容
     */
    public static String getReturnTraceFromMethodNode(MethodNode methodNode) {
        // 获得当前methodNode下的最后一个TextNode，里面包含了Return trace语句
        TextNode lastTextNode = (TextNode) (methodNode.contentNodeList.get(methodNode.contentNodeList.size() - 1));
        // 获得 Return语句所在的序号
        int returnTraceNum = getReturnTraceNumFromList(lastTextNode.tracesList);
        // 获得Return trace语句
        String returnTrace = lastTextNode.tracesList.get(returnTraceNum);
        return returnTrace;
    }

    /**
     * 等号左边如果为修改对象引用关系内容，包括Put,PutA，等号使用RefUpdate表示
     *
     * @param leftStatement  左操作数的表达式
     * @param rightStatement 右操作数的表达式
     * @return 生成的DSL语句
     */
    public static String genRefUpdateDslStatement(String leftStatement, String rightStatement) {
        String dslStatement;
        if (isGenJavaProgram) {
            // 如果直接输出的是Java程序
            dslStatement = leftStatement + " = " + rightStatement;
        } else {
            // 如果输出的是DSL程序
            dslStatement = leftStatement + " = " + rightStatement;
        }
        return dslStatement;
    }

    /**
     * 从函数描述符中获得methodNode的返回类型
     * output: Ljava/lang/StringBuilder;  V  Z  [B
     *
     * @param methodNode 要获取返回类型的函数
     * @return 返回类型
     */
    public static String getMethodReturnType(MethodNode methodNode) {
        // methodName内容 java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
        return RegularExpressionProcess(methodNode.methodName.split(" ")[1], "\\)(.*?)$");
    }

    /**
     * 清除普通对象类型前面的L与后面的;
     * input: Ljava/lang/String;
     * output: java/lang/String
     *
     * @param rawClassName 原始类型名包含L与;
     * @return 去掉L与;的普通对象类名
     */
    public static String refineClassName(String rawClassName) {
        return rawClassName.substring(1, rawClassName.length() - 1);
    }

}
